CAM scripting/de

Einführung

Der Arbeitsbereichen CAM bietet Werkzeuge zum Importieren, Erstellen, Ändern und Exportieren von Bearbeitungs-Werkzeugbahnen in FreeCAD. Mit ihnen kann der Anwender bestehende G-code-Programme importieren, visualisieren und modifizieren, Werkzeugbahnen aus 3D-Formen erstellen und diese Werkzeugbahnen in G-code exportieren.

Der Arbeitsbereich CAM befindet sich derzeit in einer frühen Entwicklungsphase und bietet nicht alle erweiterten Funktionen, die in einigen kommerziellen Alternativen zu finden sind. Die Python-Skript-Schnittstelle macht es jedoch einfach, Werkzeuge zu modifizieren oder leistungsfähigere zu entwickeln.

Schnellstart

Die Pfadobjekte von FreeCAD bestehen aus einer Folge von Bewegungsbefehlen. Eine typische Anwendung ist diese:

>>> import Path
>>> c1 = Path.Command("g1x1")
>>> c2 = Path.Command("g1y4")
>>> c3 = Path.Command("g1 x2 y2") # spaces end newlines are ignored
>>> p = Path.Path([c1,c2,c3])
>>> o = App.ActiveDocument.addObject("Path::Feature","mypath")
>>> o.Path = p
>>> print (p.toGCode())

FreeCADs internes G-Code-Format

Es ist wichtig, ein vorläufiges Konzept zu verstehen. Der Großteil der folgenden Implementierung stützt sich stark auf Bewegungsbefehle, die dieselben Namen wie G-Code-Befehle haben, jedoch nicht auf die Implementierung eines bestimmten Controllers ausgerichtet sind. Namen wie 'G0' für 'Schnellbewegung' oder 'G1' für 'Vorschubbewegung' wurden aus Gründen der Leistungsfähigkeit (effizientes Speichern von Dateien) und zur Minimierung des Aufwands für die Übersetzung in andere G-Code-Formate gewählt. Da es in der CNC-Welt Tausende von G-Code-Dialekten gibt, wurde eine stark vereinfachte Untergruppe davon ausgewählt. Man könnte das G-Code-Format von FreeCAD als eine "maschinenunabhängige" Form des G-Codes beschreiben.

Innerhalb von .FCStd-Dateien werden Pfaddaten direkt in diesem G-Code-Format gespeichert.

Alle Übersetzungen von/in Dialekte zu FreeCAD-G-Code erfolgen über Vor- und Nachbearbeitungsskripte. Das bedeutet, dass man, wenn man mit einer Maschine arbeiten möchte, die einen bestimmten LinuxCNC-, Fanuc-, Mitusubishi- oder HAAS-Controller usw. verwendet, einen Nachbearbeitungsprozessor für diesen bestimmten Controller verwendet (oder erstellt, falls nicht vorhanden) muss (siehe Abschnitt "Importieren und Exportieren von G-Code" weiter unten).

G-Code-Referenz

Die folgenden Regeln und Richtlinien definieren den G-Code-Befehlssatz, den FreeCAD intern benutzt:

  • G-code data, inside FreeCAD Path objects, is separated into "Commands". A Command is defined by a command name, which must begin with G or M, and (optionally) arguments, which are in the form Letter plus a Float, for example X 0.02 or Y 3.5 or F 300. These are examples of typical G-code commands in FreeCAD:
G0 X2.5 Y0 (The command name is G0, the arguments are X=2.5 and Y=0)
G1 X30 (The command name is G1, the only argument is X=30)
G90 (The command name is G90, there are no arguments)
  • For the numeric part of a G or M command, both "G1" or "G01" forms are supported.
  • Only commands starting with G or M are supported at the moment.
  • Only millimeters are accepted at the moment. G20/G21 are ignored.
  • Arguments are always sorted alphabetically. This means that if you create a command with "G1 X2 Y4 F300", it will be stored as "G1 F300 X2 Y4"
  • Arguments cannot be repeated inside a same command. For example, "G1 X1 Y2 X2 Y3" will not work. You will need to split it into two commands, for example: "G1 X1 Y2, G1 X2 Y3"
  • X, Y, Z, A, B, C arguments are absolute or relative, depending on the current G90/G91 mode. Default (if not specified) is absolute.
  • I, J, K are always relative to the last point. K can be omitted.
  • X, Y, or Z (and A, B, C) can be omitted. In this case, the previous X-, Y- or Z-coordinates are maintained.
  • G-code commands other than the ones listed in the table below are supported, that is, they are saved inside the path data (as long as they comply to the rules above, of course), but they simply won't produce any visible result on screen. For example, you could add a G81 command, it will be stored, but not displayed.

Liste der aktuell unterstützten G-Code Befehle

Command Description Supported Arguments Displayed
G0 rapid move X,Y,Z,A,B,C Red
G1 normal move X,Y,Z,A,B,C Green
G2 clockwise arc X,Y,Z,A,B,C,I,J,K Green
G3 counterclockwise arc X,Y,Z,A,B,C,I,J,K Green
G81, G82, G83 drill X,Y,Z,R,Q Red/Green
G38.2 Straight probe move (used in probe operation) Z,F Yellow
G90 absolute coordinates
G91 relative coordinates
(Message) comment

Das Befehlsobjekt

The Command object represents a G-code command. It has three attributes: Name, Parameters and Placement, and two methods: toGCode() and setFromGCode(). Internally, it contains only a name and a dictionary of parameters. The rest (placement and gcode) is computed to/from this data.

>>> import Path
>>> c=Path.Command()
>>> c
Command  ( )
>>> c.Name = "G1"
>>> c
Command G1 ( )
>>> c.Parameters= {"X":1,"Y":0}
>>> c
Command G1 ( X:1 Y:0 )
>>> c.Parameters
{'Y': 0.0, 'X': 1.0}
>>> c.Parameters= {"X":1,"Y":0.5}
>>> c
Command G1 ( X:1 Y:0.5 )
>>> c.toGCode()
'G1X1Y0.5'
>>> c2=Path.Command("G2")
>>> c2
Command G2 ( )
>>> c3=Path.Command("G1",{"X":34,"Y":1.2})
>>> c3
Command G1 ( X:34 Y:1.2 )
>>> c3.Placement
Placement [Pos=(34,1.2,0), Yaw-Pitch-Roll=(0,0,0)]
>>> c3.toGCode()
'G1X34Y1.2'
>>> c3.setFromGCode("G1X1Y0")
>>> c3
Command G1 [ X:1 Y:0 ]
>>> c4 = Path.Command("G1X4Y5")
>>> c4
Command G1 [ X:4 Y:5 ]
>>> p1 = App.Placement()
>>> p1.Base = App.Vector(3,2,1)
>>> p1
Placement [Pos=(3,2,1), Yaw-Pitch-Roll=(0,0,0)]
>>> c5=Path.Command("g1",p1)
>>> c5
Command G1 [ X:3 Y:2 Z:1 ]
>>> p2=App.Placement()
>>> p2.Base = App.Vector(5,0,0)
>>> c5
Command G1 [ X:3 Y:2 Z:1 ]
>>> c5.Placement=p2
>>> c5
Command G1 [ X:5 ]
>>> c5.x
5.0
>>> c5.x=10
>>> c5
Command G1 [ X:10 ]
>>> c5.y=2
>>> c5
Command G1 [ X:10 Y:2 ]

Das Bahnobjekt

Das Pfad-Objekt enthält eine Liste von Befehlen

>>> import Path
>>> c1=Path.Command("g1",{"x":1,"y":0})
>>> c2=Path.Command("g1",{"x":0,"y":2})
>>> p=Path.Path([c1,c2])
>>> p
Path [ size:2 length:3 ]
>>> p.Commands
[Command G1 [ X:1 Y:0 ], Command G1 [ X:0 Y:2 ]]
>>> p.Length
3.0
>>> p.addCommands(c1)
Path [ size:3 length:4 ]
>>> p.toGCode()
'G1X1G1Y2G1X1'

lines = """
G0X-0.5905Y-0.3937S3000M03
G0Z0.125
G1Z-0.004F3
G1X0.9842Y-0.3937F14.17
G1X0.9842Y0.433
G1X-0.5905Y0.433
G1X-0.5905Y-0.3937
G0Z0.5
"""

slines = lines.split('\n')
p = Path.Path()
for line in slines:
    p.addCommands(Path.Command(line))

o = App.ActiveDocument.addObject("Path::Feature","mypath")
o.Path = p

# but you can also create a path directly form a piece of G-code.
# The commands will be created automatically:

p = Path.Path()
p.setFromGCode(lines)

As a shortcut, a Path object can also be created directly from a full G-code sequence. It will be divided into a sequence of commands automatically.

>>> p = Path.Path("G0 X2 Y2 G1 X0 Y2")
>>> p
Path [ size:2 length:2 ]

Die Bahn Funktion

Die Pfadfunktion ist ein FreeCAD-Dokumentobjekt, das einen Pfad enthält und diesen in der 3D-Ansicht darstellt.

>>> pf = App.ActiveDocument.addObject("Path::Feature","mypath")
>>> pf
<Document object>
>>> pf.Path = p
>>> pf.Path
Path [ size:2 length:2 ]

Die Pfad Funktion besitzt auch eine Eigenschaft Positionierung. Ändern des Wertes dieser Positionierung , ändert die Position der Funktion in der 3D Ansicht, obwohl die Pfadinformationen selbst nicht verändert werden. Die Transformation ist rein visuell. Auf diese Weise kann zum Beispiel ein Pfad um eine Fläche herum erstellt werden, die eine bestimmte Ausrichtung auf dem Modell hat, die nicht die gleiche Ausrichtung ist, die das Schneidmaterial auf der CNC Maschine haben wird.

However, Path Compounds can make use of the Placement of their children (see below).

Die Werkzeug- und Werkzeugtabellen-Objekte

NOTE: This type of tool usage is depreciated as of the 0.19 official release. In 0.19 the new ToolBit tool system was implemented to supersede this older, Legacy, system. Therefore, coding has changed from what is represented below. Please visit CAM Tools page for more information.

Skripten <= 0.18

Das Tool-Objekt enthält die Definitionen eines CNC-Werkzeugs. Das Tooltable-Objekt enthält eine geordnete Liste von Werkzeugen. Werkzeugtabellen werden als Eigenschaft an Path Project-Features angehängt und können auch über die grafische Benutzeroberfläche bearbeitet werden, indem man in der Baumansicht auf ein Projekt doppelklickt und in der sich öffnenden Aufgabenansicht auf die Schaltfläche "Werkzeugtabelle bearbeiten" klickt.

From that dialog, tooltables can be imported from FreeCAD's .xml and HeeksCad's .tooltable formats, and exported to FreeCAD's .xml format.

>>> import Path
>>> t1=Path.Tool()
>>> t1
Tool Default tool
>>> t1.Name = "12.7mm Drill Bit"
>>> t1
Tool 12.7mm Drill Bit
>>> t1.ToolType
'Undefined'
>>> t1.ToolType = "Drill"
>>> t1.Diameter= 12.7
>>> t1.LengthOffset = 127
>>> t1.CuttingEdgeAngle = 59
>>> t1.CuttingEdgeHeight = 50.8
>>> t2 = Path.Tool("my other tool",tooltype="EndMill",diameter=10)
>>> t2
Tool my other tool
>>> t2.Diameter
10.0
>>> table = Path.Tooltable()
>>> table
Tooltable containing 0 tools
>>> table.addTools(t1)
Tooltable containing 1 tools
>>> table.addTools(t2)
Tooltable containing 2 tools
>>> table.Tools
{1: Tool 12.7mm Drill Bit, 2: Tool my other tool}
>>>

Funktionen

Die Pfad Verbund Funktion

Das Ziel dieser Funktion ist es, einen oder mehrere Werkzeugwege zu sammeln und sie mit einer Werkzeugtabelle zu verknüpfen. Die Funktion "Verbund" verhält sich auch wie eine Standard-FreeCAD-Gruppe, sodass man Objekte direkt aus der Baumansicht hinzufügen oder entfernen kann. Es können Elemente auch neu angeordnet werden, indem man in der Baumansicht auf das Verbund-Objekt doppelklickt und dessen Elemente in der sich öffnenden Aufgabenansicht neu anordnet.

>>> import Path
>>> p1 = Path.Path("G1X1")
>>> o1 = App.ActiveDocument.addObject("Path::Feature","path1")
>>> o1.Path=p1
>>> p2 = Path.Path("G1Y1")
>>> o2 = App.ActiveDocument.addObject("Path::Feature","path2")
>>> o2.Path=p2
>>> o3 = App.ActiveDocument.addObject("Path::FeatureCompound","compound")
>>> o3.Group=[o1,o2]

An important feature of Path Compounds is the possibility to take into account the Placement of their child paths or not, by setting their UsePlacements property to True or False. If not, the Path data of their children will simply be added sequentially. If True, each command of the child paths, if containing position information (G0, G1, etc..) will first be transformed by the Placement before being added.

Creating a compound with just one child path allows you therefore to turn the child path's Placement "real" (it affects the Path data).

Die Pfad Projekt Funktion

The Path project is an extended kind of Compound, that has a couple of additional machine-related properties such as a tooltable. It is made mainly to be the main object type you'll want to export to G-code once your whole path setup is ready. The Project object is now coded in Python, so its creation mechanism is a bit different:

>>> from PathScripts import PathProject
>>> o4 = App.ActiveDocument.addObject("Path::FeatureCompoundPython","prj")
>>> PathProject.ObjectPathProject(o4)
>>> o4.Group = [o3]
>>> o4.Tooltable
Tooltable containing 0 tools

The Path module also features a GUI tooltable editor that can be called from Python, giving it an object that has a ToolTable property:

>>> from PathScripts import TooltableEditor
>>> TooltableEditor.edit(o4)

Pfad aus Form holen

Assign the shape of wire Part to a normal Path object, using Path.fromShape() script function (or more powerful Path.fromShapes()). By giving as parameter a wire Part object, its path will be automatically calculated from the shape. Note that in this case the placement is automatically set to the first point of the wire, and the object is therefore not movable anymore by changing its placement. To move it, the underlying shape itself must be moved.

>>> import Part
>>> import Path
>>> v1 = FreeCAD.Vector(0,0,0)
>>> v2 = FreeCAD.Vector(0,2,0)
>>> v3 = FreeCAD.Vector(2,2,0)
>>> v4 = FreeCAD.Vector(3,3,0)
>>> wire = Part.makePolygon([v1,v2,v3,v4])
>>> o = FreeCAD.ActiveDocument.addObject("Path::Feature","myPath2")
>>> o.Path = Path.fromShape(wire)
>>> FreeCAD.ActiveDocument.recompute()
>>> p =  o.Path
>>> print(p.toGCode())

Python Funktionen

Both Path::Feature and Path::FeatureShape features have a Python version, respectively named Path::FeaturePython and Path::FeatureShapePython, that can be used in Python code to create more advanced parametric objects derived from them.

G-Code importieren und exportieren

Ursprungsformat

G-code files can be directly imported and exported via the GUI, by using the "open", "insert" or "export" menu items. After the file name is acquired, a dialog pops up to ask which processing script must be used. It can also be done from Python:

Path information is stored into Path objects using a subset of G-code described in the "FreeCAD's internal G-code format"section above. This subset can be imported or exported "as is", or converted to/from a particular version of G-code suited for your machine.

If you have a very simple and standard G-code program, that complies to the rules described in the "FreeCAD's internal G-code format" section above, for example the boomerang from cnccookbook, it can be imported directly into a Path object, without translation (this is equivalent to using the "None" option of the GUI dialog):

import Path
f = open("/path/to/boomerangv4.ncc")
s = f.read()
p = Path.Path(s)
o = App.ActiveDocument.addObject("Path::Feature","boomerang")
o.Path = p

In the same manner, you can obtain the path information as "agnostic" G-code, and store it manually in a file:

text = o.Path.toGCode()
print text
myfile = open("/path/to/newfile.ngc")
myfile.write(text)
myfile.close()

If you need a different output, though, you will need to convert this agnostic G-code into a format suited for your machine. That is the job of post-processing scripts.

Verwendung von Vor- und Nachbearbeitungsskripten

Wenn man eine G-Code-Datei für eine bestimmte Maschine hat, die nicht den internen Regeln von FreeCAD entspricht, die im Abschnitt "FreeCADs internes G-Code-Format" oben beschrieben sind, kann es sein, dass sie nicht richtig importiert und/oder in der 3D-Ansicht gerendert wird. Um dies zu beheben, muss ein Vorverarbeitungsskript verwendet werden, das das maschinenspezifisches Format in das FreeCAD-Format konvertiert.

Wenn der Name des zu verwendenden Vorverarbeitungsskripts bekannt ist, kann die Datei damit aus der Python-Konsole wie folgt importiert werden:

import example_pre
example_pre.insert("/path/to/myfile.ncc","DocumentName")

In the same manner, you can output a Path object to G-code, using a post_processor script like this:

import example_post
example_post.export (myObjectName,"/path/to/outputFile.ncc")

Schreiben von Verarbeitungsskripten

Pre- and post-processing scripts behave like other common FreeCAD imports/exporters. When choosing a pre/post processing script from the dialog, the import/export process will be redirected to the specified given script. Preprocessing scripts must contain at least the following methods open(filename) and insert(filename,docname). Postprocessing scripts need to implement export(objectslist,filename).

Scripts are placed into either the Mod/Path/Path/Post/scripts folder or the user's macro path directory. You can give them any name you like but by convention, and to be picked by the GUI dialog, pre-processing scripts names must end with "_pre", post-processing scripts with "_post" (make sure to use the underscore, not the hyphen, otherwise Python cannot import it). This is an example of a very, very simple preprocessor. More complex examples are found in the Mod/Path/Path/Post/scripts folder:

def open(filename):
    gfile = __builtins__.open(filename)
    inputstring = gfile.read()
    # the whole gcode program will come in as one string,
    # for example: "G0 X1 Y1\nG1 X2 Y2"
    output = ""
    # we add a comment
    output += "(This is my first parsed output!)\n"
    # we split the input string by lines
    lines = inputstring.split("\n")
    for line in lines:
        output += line
        # we must insert the "end of line" character again
        # because the split removed it
        output += "\n"
    # another comment
    output += "(End of program)"
    import Path
    p = Path.Path(output)
    myPath = FreeCAD.ActiveDocument.addObject("Path::Feature","Import")
    myPath.Path = p
    FreeCAD.ActiveDocument.recompute()

Pre- and post-processors work exactly the same way. They just do the contrary: The pre scripts convert from specific G-code to FreeCAD's "agnostic" G-code, while post scripts convert from FreeCAD's "agnostic" G-code to machine-specific G-code.

Hinzufügen aller Flächen eines FormZeichenfolge zur Liste der BasisFunktionen einer ProfilVonFlächen Operation

Dieses Beispiel basiert auf einer Diskussion im deutschsprachigen Forum.

Voraussetzungen

  • Create a solid with ShapeString as Cutout
  • Create a Job using this solid as its BaseObject
  • Create a ProfileFromFaces operation named "Profile_Faces" with empty BaseGeometry.

Der Code

Der folgende Code fügt dann alle Flächen aus ShapeString hinzu und erstellt die Pfade:

doc = App.ActiveDocument
list_of_all_element_faces = []
for i, face in enumerate(doc.ShapeString.Shape.Faces):
    list_of_all_element_faces.append('Face' + str(i + 1))

doc.Profile_Faces.Base = [(doc.ShapeString, tuple(list_of_all_element_faces))]
doc.recompute()